package es

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
)

type Metadata struct {
	Name    string
	Version int
	Size    int64
	Hash    string
}

type hit struct {
	Source Metadata `json:"_source"`
}

type searchResult struct {
	Hits struct {
		Total struct {
			Value    int    `json:"value"`
			Relation string `json:"relation"`
		} `json:"total"`
		Hits []hit `json:"hits"`
	} `json:"hits"`
}

type Bucket struct {
	Key         string
	Doc_count   int
	Min_version struct {
		Value float32
	}
}

type aggregateResult struct {
	Aggregations struct {
		Group_by_name struct {
			Buckets []Bucket
		}
	}
}

func getMetadata(name string, version int) (meta Metadata, e error) {
	url := fmt.Sprintf("http://%s/metadata/_source/%s_%d",
		os.Getenv("ES_SERVER"), name, version)
	r, e := http.Get(url)

	if e != nil {
		return
	}
	if r.StatusCode != http.StatusOK {
		e = fmt.Errorf("fail to get %s_%d: %d", name, version, r.StatusCode)
		return
	}

	result, _ := io.ReadAll(r.Body)
	json.Unmarshal(result, &meta)

	return

}

func SearchLatestVersion(name string) (meta Metadata, e error) {
	//url := fmt.Sprintf("http://%s/metadata/_search?q=objects.name:%s&size=1&sort=objects.version:desc", os.Getenv("ES_SERVER"), url.PathEscape(name))
	//url := fmt.Sprintf("http://%s/metadata/_search?q=name:%s&size=1&sort=version:desc", os.Getenv("ES_SERVER"), url.PathEscape(name))
	//fmt.Println(url)
	// r, e := http.Get(url)
	// if e != nil {
	// 	return
	// }
	baseURL := fmt.Sprintf("http://%s/metadata/_search", os.Getenv("ES_SERVER"))

	// Create URL with parameters
	u, err := url.Parse(baseURL)
	if err != nil {
		return
	}

	q := u.Query()
	q.Set("sort", "version:desc")
	q.Set("size", strconv.Itoa(1))

	if name != "" {
		q.Set("q", fmt.Sprintf("name:%s", name)) //url.QueryEscape(name)
	}
	//在 Go 程序中，url.QueryEscape 会对 : 和其他特殊字符进行额外编码，比如将 : 转换为 %3A。

	//这种多余的编码可能导致 Elasticsearch 解析 q=objects.name%253Atest3（双重编码的 %3A）时找不到结果。

	u.RawQuery = q.Encode() //encode
	//fmt.Println(u.String())
	//r, e := http.Get(url)
	// r, e := http.Get(u.String())
	// if e != nil {
	// 	return nil, e
	// }

	req, err := http.NewRequest("GET", u.String(), bytes.NewBuffer(nil))
	if err != nil {
		fmt.Println("Error creating request:", err)
		return
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	if resp.StatusCode != http.StatusOK {
		result, _ := io.ReadAll(resp.Body)
		e = fmt.Errorf("fail to search metadatas: %d, %s", resp.StatusCode, string(result))
		return
	}
	var sr searchResult

	result, _ := io.ReadAll(resp.Body)
	//fmt.Printf("get latest version result: %d, %s", r.StatusCode, string(result))

	json.Unmarshal(result, &sr)
	if len(sr.Hits.Hits) != 0 {
		return sr.Hits.Hits[0].Source, nil
	}

	return

}
func GetMetadata(name string, version int) (Metadata, error) {
	if version == 0 {
		return SearchLatestVersion(name)
	}
	return getMetadata(name, version)
}

func AddVersion(name, hash string, size int64) error {
	version, e := SearchLatestVersion(name)
	if e != nil {
		return e
	}
	return PutMetadata(name, version.Version+1, size, hash)

}

func PutMetadata(name string, version int, size int64, hash string) error {
	doc := fmt.Sprintf(`{"name": "%s", "version": %d, "size": %d, "hash": "%s"}`, name, version, size, hash)

	client := http.Client{}
	url := fmt.Sprintf("http://%s/metadata/_doc/%s_%d?op_type=create", os.Getenv("ES_SERVER"), name, version)
	request, _ := http.NewRequest("PUT", url, strings.NewReader(doc))
	request.Header.Set("Content-Type", "application/json")

	r, e := client.Do(request)
	if e != nil {
		return e
	}

	if r.StatusCode == http.StatusConflict {
		return PutMetadata(name, version+1, size, hash)
	}
	if r.StatusCode != http.StatusCreated {

		result, _ := io.ReadAll(r.Body)
		return fmt.Errorf("fail to put metadata: %d, %s", r.StatusCode, string(result))
	}

	return nil

}

func SearchAllVersions(name string, from, size int) ([]Metadata, error) {

	// url := fmt.Sprintf("http://%s/metadata/_search?sort=objects.name,objects.version&from=%d&size=%d", os.Getenv("ES_SERVER"), from, size)

	// //curl -v http://localhost:9200/metadata/_search?sort=objects.name,objects.version&from=0&size=10&q=objects.name:test3
	// if name != "" {
	// 	url += "&q=objects.name:" + name //fixme
	// 	//url1 = url.EscapedPath(url1)

	// }
	// fmt.Println(url)
	// Build base URL
	baseURL := fmt.Sprintf("http://%s/metadata/_search", os.Getenv("ES_SERVER"))

	// Create URL with parameters
	u, err := url.Parse(baseURL)
	if err != nil {
		return nil, fmt.Errorf("failed to parse base URL: %w", err)
	}

	q := u.Query()
	q.Set("sort", "objects.name,objects.version")
	q.Set("from", strconv.Itoa(from))
	q.Set("size", strconv.Itoa(size))

	if name != "" {
		q.Set("q", fmt.Sprintf("name:%s", name)) //url.QueryEscape(name)
	}
	//在 Go 程序中，url.QueryEscape 会对 : 和其他特殊字符进行额外编码，比如将 : 转换为 %3A。

	//这种多余的编码可能导致 Elasticsearch 解析 q=objects.name%253Atest3（双重编码的 %3A）时找不到结果。

	u.RawQuery = q.Encode() //encode
	//fmt.Println(u.String())
	//r, e := http.Get(url)
	// r, e := http.Get(u.String())
	// if e != nil {
	// 	return nil, e
	// }

	req, err := http.NewRequest("GET", u.String(), bytes.NewBuffer(nil))
	if err != nil {
		fmt.Println("Error creating request:", err)
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("Error:", err)
		return nil, err
	}

	metas := make([]Metadata, 0)

	result, _ := io.ReadAll(resp.Body)
	//fmt.Printf("get all version result: %d, %s", resp.StatusCode, string(result))

	var sr searchResult
	json.Unmarshal(result, &sr)

	for i := range sr.Hits.Hits {
		metas = append(metas, sr.Hits.Hits[i].Source)
	}
	return metas, nil
}

func SearchVersionStatus(min_doc_count int) ([]Bucket, error) {
	client := http.Client{}
	url := fmt.Sprintf("http://%s/metadata/_search", os.Getenv("ES_SERVER"))
	body := fmt.Sprintf(`{
		"size": 0,
		"aggs": {
			"group_by_name": {
				"terms": {
					"field": "name.keyword",
					"min_doc_count": %d
				},
				"aggs": {
					"min_version": {
						"min": {
							"field": "version"
						} 
					}
				}
			}
		}
	}`, min_doc_count)

	req, _ := http.NewRequest("GET", url, strings.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	r, e := client.Do(req)
	if e != nil {
		log.Println(e)
		return nil, e
	}
	b, _ := io.ReadAll(r.Body)
	fmt.Printf("get all  results: %d, %s", r.StatusCode, string(b))
	var ar aggregateResult
	json.Unmarshal(b, &ar)
	return ar.Aggregations.Group_by_name.Buckets, nil

}

func DelMetadata(name string, version int) {
	client := http.Client{}
	url := fmt.Sprintf("http://%s/metadata/_doc/%s_%d",
		os.Getenv("ES_SERVER"), name, version)
	req, _ := http.NewRequest("DELETE", url, nil)
	r, e := client.Do(req)
	if e != nil {
		log.Println(e)
		return
	}
	b, _ := io.ReadAll(r.Body)
	fmt.Printf("del results: %d, %s", r.StatusCode, string(b))

}

func HasHash(hash string) (bool, error) {
	baseURL := fmt.Sprintf("http://%s/metadata/_search", os.Getenv("ES_SERVER"))

	// Create URL with parameters
	u, err := url.Parse(baseURL)
	if err != nil {
		return false, err
	}

	// q := u.Query()
	// q.Set("hash", hash)
	// q.Set("size", strconv.Itoa(0))
	// //在 Go 程序中，url.QueryEscape 会对 : 和其他特殊字符进行额外编码，比如将 : 转换为 %3A。

	// //这种多余的编码可能导致 Elasticsearch 解析 q=objects.name%253Atest3（双重编码的 %3A）时找不到结果。

	// u.RawQuery = q.Encode() //encode
	// //fmt.Println(u.String())
	// //r, e := http.Get(url)
	// // r, e := http.Get(u.String())
	// // if e != nil {
	// // 	return nil, e
	// // }
	// fmt.Println(u.String())
	// Avoid double encoding by directly setting the query string
	q := fmt.Sprintf("hash:%s", hash)
	u.RawQuery = fmt.Sprintf("q=%s&size=%d", q, 0)

	// Print constructed URL for debugging
	//fmt.Println("Constructed URL:", u.String())

	req, err := http.NewRequest("GET", u.String(), bytes.NewBuffer(nil))
	if err != nil {
		fmt.Println("Error creating request:", err)
		return false, err
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("Error:", err)
		return false, err
	}

	b, _ := io.ReadAll(resp.Body)
	//fmt.Printf("get all  results: %d, %s\n", resp.StatusCode, string(b))
	var sr searchResult
	json.Unmarshal(b, &sr)
	//fmt.Printf("sr.Hits.Total.Value: %d\n", sr.Hits.Total.Value)
	return sr.Hits.Total.Value != 0, nil

}

func SearchHashSize(hash string) (size int64, e error) {
	baseURL := fmt.Sprintf("http://%s/metadata/_search", os.Getenv("ES_SERVER"))

	// Create URL with parameters
	u, err := url.Parse(baseURL)
	if err != nil {
		return
	}

	// q := u.Query()
	// q.Set("hash", hash)
	// q.Set("size", strconv.Itoa(1))

	//在 Go 程序中，url.QueryEscape 会对 : 和其他特殊字符进行额外编码，比如将 : 转换为 %3A。

	//这种多余的编码可能导致 Elasticsearch 解析 q=objects.name%253Atest3（双重编码的 %3A）时找不到结果。

	//u.RawQuery = q.Encode() //encode
	//fmt.Println(u.String())
	//r, e := http.Get(url)
	// r, e := http.Get(u.String())
	// if e != nil {
	// 	return nil, e
	// }

	q := fmt.Sprintf("hash:%s", hash)
	u.RawQuery = fmt.Sprintf("q=%s&size=%d", q, 1)

	//fmt.Println("Constructed URL:", u.String())
	req, err := http.NewRequest("GET", u.String(), bytes.NewBuffer(nil))
	if err != nil {
		fmt.Println("Error creating request:", err)
		return
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	if resp.StatusCode != http.StatusOK {
		//result, _ := io.ReadAll(resp.Body)
		e = fmt.Errorf("fail to search hash size: %d", resp.StatusCode)
		return
	}
	result, _ := io.ReadAll(resp.Body)
	//fmt.Printf("get all  results: %d, %s\n", resp.StatusCode, string(result))
	var sr searchResult
	json.Unmarshal(result, &sr)
	if len(sr.Hits.Hits) != 0 {
		size = sr.Hits.Hits[0].Source.Size
		//fmt.Println("sr:", sr)
		//fmt.Println("size:", size)
	}

	return
}
